package org.fjsei.yewu.graphql;

import graphql.PublicApi;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.DelegatingDataFetchingEnvironment;

import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;


//修改底层 参考来自： graphql-java-18.1-sources.jar!/graphql/schema/DataFetcherFactories.java

/** 其实算工具 ：帮助DataFetcherFactory的。
 * 给自定义的 Directive 目录的类使用。
 * A helper for {@link graphql.schema.DataFetcherFactory}
 */
@PublicApi
public class MyDataFetcherFactories {

    /**
     * This helper function allows you to wrap an existing data fetcher and map the value once it completes.  It helps you handle
     * values that might be {@link  java.util.concurrent.CompletionStage} returned values as well as plain old objects.
     *
     * @param delegateDataFetcher the original data fetcher that is present on a {@link graphql.schema.GraphQLFieldDefinition} say
     * @param mapFunction     是回调钩子，不会立刻执行的。
     *
     * @return a new data fetcher that wraps the provided data fetcher
    底层有错误！
     用于 onArgument @directive 注入; 参数场景。
    在DataFetcher执行之前率先 执行钩子函数。
     【缺点】只能针对接口传入参数进行检查抛出异常，无法改动参数值的。
     */
    public static DataFetcher beforeDataFetcher(String argumentName,DataFetcher delegateDataFetcher, BiFunction<DataFetchingEnvironment, Object, Object> mapFunction) {
        return environment -> {
            //String argumentName=environment.getFieldDefinition().getArguments().get(0).getName();
            //Boolean matched=environment.getFieldDefinition().getArguments().get(0).getDirectives().get(0).getName().equals(directiveName);
            Object argumentValue =environment.getArgument( argumentName );
              //上一步取旧的前端给的参数：retArgumentValue才是想要的结果参数，参数@date注解处理流程后回到这里了， argumentValue应该转换成新的数据类型！
            Object retArgumentValue =mapFunction.apply(environment, argumentValue);
            //上面旧的［用途］针对Directive时刻的；就不能修改入口输入参数传递的值，只能对参数进行检查，抛出异常。
            //不允许修改Arguments的？  DelegatingDataFetchingEnvironment 也是一样不能改啊！
            //environment.getArguments().put(argumentName, retArgumentValue);  报错只能读的不能更改啊！ environment.getVariables()也是不能改的;
            //真正to fetch the value从ORM数据库取得。  到了一步：参数已经给接口了，但接口函数还没运行啊
            Object value =delegateDataFetcher.get(environment);
            //Object value =retArgumentValue; //delegateDataFetcher.get(environment);   ？？直接把参数当成结果扔回来

            //接口调用还没进入接口的，这里就看到了报错value=MonoError ：Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-08-07'
            if (value instanceof CompletionStage) {
                //return ((CompletionStage<Object>) value).thenApply(v -> mapFunction.apply(environment, v));
                return ((CompletionStage<Object>) value).thenApply(v -> v);
            } else {
                //return mapFunction.apply(environment, value);
                return value;
            }
        };
    }

    //权限检查版本的。
    public static DataFetcher permitDataFetcher(DataFetcher delegateDataFetcher, BiFunction<DataFetchingEnvironment, Object, Object> mapFunction) {
        return environment -> {
            //检查回调函数
            Object retArgumentValue =mapFunction.apply(environment, null);
            //［用途］针对Directive时刻的；就不能修改入口输入参数传递的值，只能对参数进行检查，抛出异常。

            //真正to fetch the value从ORM数据库取得。
            Object value = delegateDataFetcher.get(environment);
            if (value instanceof CompletionStage) {
                //return ((CompletionStage<Object>) value).thenApply(v -> mapFunction.apply(environment, v));
                return ((CompletionStage<Object>) value).thenApply(v -> v);
            } else {
                //return mapFunction.apply(environment, value);
                return value;
            }
        };
    }

    /**加@dbpage指示,跳转接口函数。 把@Entity底下字段函数转移到GraphQLQueryResolver底下的接口，就能注入@Autowired其它,实现直接在数据库分页。
     * 返回新生成的DataFetcher实际就是(environment)=>{}的包装函数。
     * delegateDataFetcher代表旧的需要更改的DataFetcher；
     * 最后参数mapFunction实际是提供注入逻辑。
     * */
    public static DataFetcher pageDataFetcher(String fieldName,DataFetcher delegateDataFetcher, BiFunction<DataFetchingEnvironment, Object, Object> mapFunction) {
        return environment -> {
            //检查回调函数
            Object retValue =mapFunction.apply(environment, fieldName);

            if (retValue instanceof CompletionStage) {
                //加入异步的 + 未完成的：
                return ((CompletionStage<Object>) retValue).thenApply(v -> v);
            } else {
                //return mapFunction.apply(environment, value);
                return retValue;
            }
        };
    }
}
